#include "copyright.h"
#include "BoundedBuffer.h"
#include "synch.h"
#include "system.h"

//----------------------------------------------------------------------
// BoundedBuffer::BoundedBuffer
//  Initialize a bounded buffer, so that it can be used for 
//  bounded producer/consumer problem.
//
//  "maxsize" is the max size of the bounded buffer.
//----------------------------------------------------------------------
BoundedBuffer::BoundedBuffer(int maxsize)
{
	buffer = (void*) AllocBoundedArray(maxsize);
	ASSERT(buffer != NULL);
	max_size = maxsize;
	fill_ptr = 0;
	use_ptr = 0;
	count = 0;
	mutex = new Lock("mutex-lock");
	empty = new Condition("empty-CV");
	fill = new Condition("fill-CV");
}

//----------------------------------------------------------------------
// BoundedBuffer::~BoundedBuffer
//  De-allocate bounded buffer, when no longer needed. 
//----------------------------------------------------------------------
BoundedBuffer::~BoundedBuffer()
{
	DeallocBoundedArray((char*) buffer, max_size);
	delete mutex;
	delete empty;
	delete fill;
}

//----------------------------------------------------------------------
// BoundedBuffer::Read
//  The consumer receives the bytes in the buffer through read in the same
//   order as the producer writes the bytes;
//  Cannot allow two threads to read the buffer at the same time;
//  If the consumer reads data too fast (that is, the number of valid bytes
//   in the buffer is less than 0), read will put the consumer to sleep
//   until the producer can write some data from the buffer.
//
// Read 'size' bytes from the bounded buffer, storing into 'data'.
// ('size' may be greater than 'maxsize')
//----------------------------------------------------------------------
void BoundedBuffer::Read(void *data, int size)
{
	int i = 0;
	while (i < size)	// loop to read size bytes
	{
		mutex->Acquire();	// ensure exclusive access, that is,
							// cannot allow two threads to read
							// the buffer at the same time

		while (count == 0)
			empty->Wait(mutex);	// condition not allow, 
								// put thread itself to sleep
		// read one byte
		memcpy(data + i, (void*)((char*) buffer + use_ptr), 1);
		use_ptr = (use_ptr + 1) % max_size;
		count--;

		DEBUG('b', "Thread %s read from buffer: \'%c\'\n", currentThread->getName(), *((char*) data + i));
		
		currentThread->Yield();	// Yield to ensure concurrent

		fill->Signal(mutex);	// wake up a Write thread
								// to avoid a deadlock
		mutex->Release();	// release the mutex lock
		i++;
	}
}


//----------------------------------------------------------------------
// BoundedBuffer::Write
//  The producer writes data to the buffer in order through write;
//  Two threads cannot write to the buffer at the same time;
//  If the producer generates data too fast (that is, the number of bytes
//   in the buffer exceeds the number of maxsize bytes), write puts the
//   producer to sleep until the consumer can capture and read some data
//   from the buffer, freeing up space.
//
// Write 'size' bytes from 'data' into the bounded buffer.
// ('size' may be greater than 'maxsize')
//----------------------------------------------------------------------
void BoundedBuffer::Write(void *data, int size)
{
	int i = 0;
	while (i < size)	// loop to read size bytes
	{
		mutex->Acquire();	// ensure exclusive access, that is,
							// cannot allow two threads to write
							// the buffer at the same time

		while (count == max_size)
			fill->Wait(mutex);	// condition not allow, 
								// put thread itself to sleep
		
		// write one byte
		memcpy((void*)((char*) buffer + fill_ptr), data + i, 1);
		fill_ptr = (fill_ptr + 1) % max_size;
		count++;

		DEBUG('b', "Thread %s write into buffer: \'%c\'\n", currentThread->getName(), *((char*) data + i));
		
		currentThread->Yield();	// Yield to ensure concurrent

		empty->Signal(mutex);	// wake up a Read thread
								// to avoid a deadlock
		mutex->Release();	// release the mutex lock
		i++;
	}
}
